<route lang="json5">
{
  style: {
    navigationStyle: 'custom',
  },
}
</route>

<template>
  <view>
    <!-- 练习区域 -->
    <view v-if="state.page === 'practice'">
      <!-- 导航栏 -->
      <wd-navbar
        left-arrow
        safeAreaInsetTop
        :title="props.practiceInfo.name"
        @click-left="handleClickLeft"
      ></wd-navbar>
      <!-- 当前题目 -->
      <view class="currentTopic" v-if="state.topicList.length > 0">
        <view v-for="(item, index) in [state.topicList[state.currentTopicIndex]]" :key="index">
          <!-- 题干 -->
          <view class="theTitle">
            <text class="quTypeName">{{ item.quTypeName }}</text>
            <text>{{ Number(item.sort) }}、</text>
            <text>{{ item.content }}</text>
          </view>
          <!-- 题型判断 -->
          <view>
            <!-- 单选题/判断题 -->
            <wd-radio-group
              v-if="item.quType === '1' || item.quType === '3'"
              v-model="item.studentAnswer"
              shape="dot"
              icon-placement="left"
              checked-color="#3670F7"
              :disabled="item.disabled"
            >
              <wd-radio v-for="(e, i) in item.options" :key="e.optionsId" :value="e.value">
                <text>
                  {{ String.fromCharCode(i + 65) }}、
                  {{ e.content }}
                </text>
                <text v-if="`${e.isRight}` === 'true' && item.showAnswer" class="isRight">
                  <wd-icon name="check-circle-filled" size="30rpx" color="#52C41A"></wd-icon>
                  答案
                </text>
              </wd-radio>
            </wd-radio-group>
            <!-- 多选题 -->
            <wd-checkbox-group
              v-if="item.quType === '2'"
              v-model="item.studentAnswer"
              cell
              :disabled="item.disabled"
            >
              <wd-checkbox
                v-for="(e, i) in item.options"
                :key="e.optionsId"
                :modelValue="e.value"
                shape="square"
              >
                <text>
                  {{ String.fromCharCode(i + 65) }}、
                  {{ e.content }}
                </text>
                <text v-if="`${e.isRight}` === 'true' && item.showAnswer" class="isRight">
                  <wd-icon name="check-circle-filled" size="30rpx" color="#52C41A"></wd-icon>
                  答案
                </text>
              </wd-checkbox>
            </wd-checkbox-group>
            <!-- 简答题 -->
            <view v-if="item.quType === '4'">
              <wd-textarea v-model="item.studentAnswer" placeholder="请输入内容"></wd-textarea>
              <!-- 标准答案 -->
              <view v-if="item.showAnswer">
                <view class="mt-16rpx fw-700">标准答案:</view>
                <wd-textarea
                  v-model="item.standardAnswer"
                  disabled
                  custom-class="textarea-standardAnswer"
                ></wd-textarea>
              </view>
            </view>
            <!-- 画图题 -->
            <view v-if="item.quType === '5'">
              <view v-for="(e, i) in item.options" :key="item.quId + i">
                <img :src="e.content" class="w-full" />
              </view>
              <p class="mt-20rpx">
                <wd-icon name="warning" size="28rpx" color="#FF8400"></wd-icon>
                <span class="ml-12rpx">参考答案：无需作答</span>
              </p>
            </view>
          </view>
          <!-- 结束训练/确认答案 -->
          <view class="flex justify-between mt-45rpx">
            <wd-button
              v-if="state.startPracticeBtnShowYn"
              custom-class="end-practice-btn"
              plain
              @click="handleStartPractice"
            >
              开始训练
            </wd-button>
            <wd-button custom-class="end-practice-btn" plain @click="handleEndPractice" v-else>
              结束训练
            </wd-button>
            <wd-button
              v-if="
                (((item.quType === '1' || item.quType === '3' || item.quType === '4') &&
                  item.studentAnswer) ||
                  (item.quType === '2' && item.studentAnswer.length > 0)) &&
                !state.startPracticeBtnShowYn
              "
              custom-class="confirm-answer-btn"
              @click="handleConfirmAnswer"
            >
              确认答案
            </wd-button>
          </view>
          <!-- 上一题/下一题 -->
          <view class="flex justify-between mt-120rpx">
            <wd-button
              v-if="state.currentTopicIndex !== 0"
              custom-class="prev-btn fw-700"
              plain
              @click="handleChangeTopic('prev')"
            >
              上一题
            </wd-button>
            <wd-button
              v-if="state.currentTopicIndex !== state.topicList.length - 1"
              custom-class="next-btn fw-700"
              @click="handleChangeTopic('next')"
            >
              下一题
            </wd-button>
          </view>
        </view>
      </view>
      <!-- 对错、答题卡入口 -->
      <view
        class="bg-[#F5F5F7] color-[#333] px-40rpx py-20rpx flex"
        :class="
          props.practiceInfo.quType !== '4' && props.practiceInfo.quType !== '5'
            ? 'justify-between'
            : 'justify-end'
        "
      >
        <view
          v-if="props.practiceInfo.quType !== '4' && props.practiceInfo.quType !== '5'"
          class="flex gap-54rpx text-center"
        >
          <view>
            <wd-icon name="check-circle-filled" size="36rpx" color="#52C41A"></wd-icon>
            <view>{{ state.rightTopicNum }}</view>
          </view>
          <view>
            <wd-icon name="close-circle-filled" size="36rpx" color="#F73A4A"></wd-icon>
            <view>{{ state.errorTopicNum }}</view>
          </view>
        </view>
        <view class="text-center" @click="handleToSheet">
          <img :src="answerSheetSvg" class="w-36rpx h-36rpx" />
          <view>答题卡</view>
        </view>
      </view>
    </view>
    <!-- 答题卡区域 -->
    <AnswerSheet
      v-else
      :sheet-info="state.sheetInfo"
      @back="state.page = 'practice'"
      @toThisTopic="handleToThisTopic"
    />
  </view>
</template>

<script setup lang="ts">
/* 引入 方法 - 获取用户信息 */
import { useUserStore } from '@/store'
/* 引入 深拷贝 方法 */
import { cloneDeep } from 'lodash-es'
/* 引入 接口 */
import {
  getTopicsOfNoProgressApi,
  getTopicsOfHaveProgressApi,
  getTopicsOfErrorApi,
  endPracticeApi,
  endErrorPracticeApi,
  topicsInfoApi,
} from '@/service/edu/question/index'
/* 引入 图片 - 答题卡 */
import answerSheetSvg from '@/static/images/question/answerSheet.svg'
/* 引入 组件 - 答题卡 */
import AnswerSheet from './answer-sheet.vue'

const props = defineProps({
  practiceInfo: {
    type: Object,
    required: true,
  },
})

const emit = defineEmits(['back', 'reload'])

/* 定义数据 */
const state = reactive<{
  page: string
  topicList: any[]
  currentTopicIndex: number
  rightTopicNum: number
  errorTopicNum: number
  correctRate: number
  sheetInfo: any[]
  startPracticeBtnShowYn: boolean
}>({
  page: 'practice', // 当前页面
  topicList: [], // 题目数组
  currentTopicIndex: 0, // 当前题目下标
  rightTopicNum: 0, // 正确题数
  errorTopicNum: 0, // 错误题数
  correctRate: 0, // 正确率
  sheetInfo: [], // 答题卡状态数据
  startPracticeBtnShowYn: false, // 开始训练按钮是否展示
})

/* 顶部导航栏 - 返回 */
async function handleClickLeft() {
  /* 返回 代表 结束训练 */
  try {
    await handleEndPractice()
  } catch (e) {
    console.error('结束训练出错：', e)
  } finally {
    emit('back')
  }
}

/* 题干乱序方法 */
function randomSort() {
  return Math.random() - 0.5
}

/* 获取 所有题目数据 */
async function getTopicList() {
  const { name, practiceMode, isAdd, repoId, quType = undefined } = props.practiceInfo
  /* 调取接口分为 3 种情况，调取接口和传参皆不相同
   * noProgress：🈚️进度模式
   * haveProgress：🈶️进度模式
   * error：错题模式
   */
  let apiType = ''
  let params = {}
  if (name.includes('错题')) {
    /* 展示开始训练按钮 */
    state.startPracticeBtnShowYn = true
    apiType = 'error'
    params = {
      isRight: 'false', // 代表错题模式
      repoId, // 题库ID
    }
  } else {
    apiType = isAdd ? 'haveProgress' : 'noProgress'
    params = {
      quType: isAdd ? undefined : quType || undefined, // 题型
      repoId, // 题库ID
      practiceMode, // 练习模式（顺序练习、随机练习、单选、多选、判断、简答、画图）
    }
  }
  const apiEnum = {
    noProgress: getTopicsOfNoProgressApi,
    haveProgress: getTopicsOfHaveProgressApi,
    error: getTopicsOfErrorApi,
  }
  try {
    const res = await apiEnum[apiType](params)
    /* 获取 用户ID */
    const studentId = await useUserStore().userInfo.userId
    /* 后端原本数据 */
    let arr: any
    let currentId: number | string
    if (res?.code !== 200) {
      state.topicList = []
      return
    } else {
      /* 🈶️进度模式 */
      if (apiType === 'haveProgress') {
        const { modeSstats = {}, questions = [] } = res.data
        state.rightTopicNum = modeSstats?.correctQuestions || 0
        state.errorTopicNum = modeSstats?.errorQuestions || 0
        state.correctRate = modeSstats?.accuracy || 0
        arr = questions
        currentId = res.data.currentId
      } else {
        arr = res.data || []
      }
    }
    /* 改造后端数据 - 这里都是调完接口的初始值
        ~ studentId
        ! practiceMode: 练习模式
        @ sort: 题目序号
        ^ quId: 题目ID
        & disabled: 错题模式下禁用
        * status: 题目状态 - 1未做 2正在做 3已做未确认答案 4已做并确认答案
        ~ showAnswer: 是否显示答案 - 点击确认答案后需要显示答案
    */
    state.topicList = arr.map((e: any, i: number) => {
      /*
        ~ 1. 选项 options 改造
      */
      if (e.options.length > 0) {
        e.options = e.options.map((o: any, value: number) => ({
          ...o,
          value: value + 1, // 选项序号
          id: o.optionsId,
          checked:
            apiType === 'error' ? e.studentAnswer && e.studentAnswer.includes(`${value}`) : false,
        }))
      } else {
        e.options = [
          {
            content: e.quType === '5' ? e.standardAnswer : '',
          },
        ]
      }
      /*
        ! 2. studentAnswer 改造
      */
      if (apiType === 'noProgress') {
        e.studentAnswer = e.quType === '2' ? [] : null
      }
      return {
        ...e,
        studentId, // 学生ID
        practiceMode, // 练习模式
        sort: i + 1, // 试题序号
        quId: e.id || e.quId, // quId 需要 特殊处理
        active: false, // 是否点击了当前题目编号(答题卡)
        disabled: apiType === 'error' || `${e.disabled}` === 'true', // 错题模式下 选项 需禁用
        /* 状态分为 4 种，用 4 种颜色区分
         # 1：未做
         ^ 2: 正在做
         ! 3: 已做但未确认答案
         ~ 4：已做并确认答案
          不同模式下，状态也不同
          🈚️进度模式：状态必然都为 1
          🈶️进度模式：状态可能为 1、2、3、4，保持后端返回数据即可
          错题模式(开始训练)：状态可能为 1、 2、3、4，保持后端返回数据即可
        */
        status: apiType === 'noProgress' ? 1 : e.status ? Number(e.status) : 1,
        /* 后端不识别 boolean，所以转为 string 进行判断 */
        showAnswer: `${e.showAnswer}` === 'true' || state.startPracticeBtnShowYn,
        /* 学生答案最终需要转换为 数字类型 */
        studentAnswer: e.studentAnswer ? Number(e.studentAnswer) : null,
      }
    })
    /* 继续改造后端数据
      ~ 添加字段 correctAnswer: 标准答案(用以判断做对做错)
    */
    state.topicList.forEach((e) => {
      const arr = e.options.filter((a: any) => a.isRight === 'true')
      /* 单选、判断 */
      if (e.quType === '1' || e.quType === '3') {
        e.correctAnswer = `${arr[0].value}` || ``
      }
      /* 多选题 */
      if (e.quType === '2') {
        e.correctAnswer = arr.map((a: any) => a.value).join(',') || ''
      }
    })

    /* 随机练习 - 开启乱序 (仅题干乱序) */
    if (name === '随机练习') {
      state.topicList.sort(randomSort).forEach((e, i) => {
        e.sort = i + 1
      })
    }

    /* 🈚️进度模式 - 默认第一题选中 */
    if (apiType === 'noProgress' || apiType === 'error') {
      state.topicList[0].active = true
    }

    /* 🈶️进度模式 - 回到上次结束练习的题的位置 */
    if (apiType === 'haveProgress') {
      state.currentTopicIndex = currentId
        ? state.topicList.findIndex((s) => {
            return s.quId === currentId
          })
        : 0
      state.topicList.length > 0 && (state.topicList[state.currentTopicIndex || 0].active = true)
    }

    console.log(state.topicList, 'state.topicList - 初始化最终版', props.practiceInfo)
  } catch (error) {
    // 处理接口调用失败的情况
    console.error(error)
  }
}

/* 上一题/下一题 */
function handleChangeTopic(type: string) {
  /* 改变 答题卡 编号 */
  state.currentTopicIndex =
    type === 'prev' ? state.currentTopicIndex - 1 : state.currentTopicIndex + 1
  /* 同步 - 改变答题卡 active */
  handleClickTopicNum(state.topicList[state.currentTopicIndex])
}

/* 点击题目编号，改变答题卡 active */
function handleClickTopicNum(e: any) {
  state.currentTopicIndex = e.sort - 1
  /* 改变 active */
  state.topicList.forEach((a: any) => {
    a.active = false
    if (a.quId === e.quId) {
      a.active = true
    }
  })
}

/* 确认答案 */
function handleConfirmAnswer() {
  /* 当前题目信息 */
  const currentObj = state.topicList[state.currentTopicIndex]
  /* 是否显示答案 */
  currentObj.showAnswer = true
  /* 确认答案之后禁用选项 */
  currentObj.disabled = true
  /* 统计该题目结果：答对 or 答错 */
  getTopicsResult(currentObj)
}

/* 判断 2 个数字数组是否完全一致 - 用来判断多选题是否回答正确 */
function areArraysEqual(arr1: number[] | string[], arr2: string[] | number[]): boolean {
  arr1 = arr1.map((e: number | string) => Number(e))
  arr2 = arr2.map((e: number | string) => Number(e))
  if (arr1.length !== arr2.length) {
    return false
  }
  for (let i = 0; i < arr1.length; i++) {
    if (arr1[i] !== arr2[i]) {
      return false
    }
  }
  return true
}

/* 答对 n 题、答错 n 题 */
function getTopicsResult(currentObj: any) {
  /* 是否正确，用以显示 答对、答错、正确率 */
  let isRight: any = false
  /* 单选、判断 */
  if (currentObj.quType === '1' || currentObj.quType === '3') {
    isRight = currentObj.studentAnswer === Number(currentObj.correctAnswer)
  }
  /* 多选 */
  if (currentObj.quType === '2') {
    isRight = areArraysEqual(currentObj.studentAnswer, currentObj.correctAnswer.split(','))
  }
  /* 最终赋值 - 画图、简答 默认传 null，后端不识别 boolean，所以要转 string */
  currentObj.isRight = currentObj.quType === '4' || currentObj.quType === '5' ? null : `${isRight}`

  /* 正确题数(目前只计入单选、多选、判断) */
  state.rightTopicNum = state.topicList.filter(
    (e) => e.isRight === 'true' && e.quType !== '4' && e.quType !== '5',
  ).length
  /* 错误题数(目前只计入单选、多选、判断) */
  state.errorTopicNum = state.topicList.filter(
    (e) => e.isRight === 'false' && e.disabled && e.quType !== '4' && e.quType !== '5',
  ).length
  /* 正确率 */
  state.correctRate = Number(
    ((state.rightTopicNum / (state.rightTopicNum + state.errorTopicNum)) * 100).toFixed(2),
  )
}

/* 进入答题卡区域 */
function handleToSheet() {
  const data = cloneDeep(state.topicList)

  const newArray = {}
  data.forEach((item) => {
    if (!newArray[item.quTypeName]) {
      newArray[item.quTypeName] = {
        quTypeName: item.quTypeName,
        data: [],
      }
    }
    newArray[item.quTypeName].data.push(item)
  })

  state.sheetInfo = Object.values(newArray)
  console.log(state.sheetInfo, 'state.sheetInfo - 123')

  state.page = 'sheet'
}

/* 从答题卡跳转到某个题目 */
function handleToThisTopic(index: number) {
  state.currentTopicIndex = index
  state.page = 'practice'
}

/* 错题模式 - 开始训练 */
function handleStartPractice() {
  state.startPracticeBtnShowYn = false
  state.topicList.forEach((e) => {
    e.showAnswer = false // 答案不显示
    e.disabled = false // 禁用解除
    e.studentAnswer = null // 答案恢复
    if (e.quType !== '4' || e.quType !== '5') {
      e.options.length > 0 &&
        e.options.forEach((a: any) => {
          a.checked = false // 选中状态去除
        })
    }
    e.status = 1 // 状态变为未做
  })
}

/* 结束训练 - 上方导航栏返回也要调取此方法 */
async function handleEndPractice() {
  /* 分为以下几种情况
    ~ 1. 随机练习 - 直接返回
    ! 2. 错题模式 - 调取错题保存接口
    ^ 3. 非错题模式 - 调取 endPracticeApi 接口
    此外，还要调取
     * topicsInfoApi 接口 - 保存 总题数、正确题数、错误题数、正确率、当前题id
  */
  if (Number(props.practiceInfo.practiceMode) === 2) {
    /* 随机练习 - 直接返回 */
    emit('back')
    return
  }
  if (props.practiceInfo.name.includes('错题')) {
    await endErrorPracticeApi(state.topicList)
  } else {
    await endPracticeApi(state.topicList)
  }
  /* 保存 总题数、正确题数、错误题数、正确率、当前题id */
  await getTopicsInfo()
  /* 返回 */
  emit('reload')
}

/* 结束训练 - 调取 topicsInfoApi - 保存所有题目的一些信息 */
async function getTopicsInfo() {
  const data = {
    studentId: await useUserStore().userInfo.userId, // 学生ID
    repoId: props.practiceInfo.repoId, // 题库ID
    practiceMode: props.practiceInfo.practiceMode, // 练习模式（顺序练习、随机练习、单选、多选、判断、简答、画图）
    totalQuestions: state.topicList.length, // 总题数
    correctQuestions: state.rightTopicNum, // 正确题数
    errorQuestions: state.errorTopicNum, // 错误题数
    accuracy: state.correctRate, // 正确率（百分比）
    currentQuestionId: state.topicList[state.currentTopicIndex].quId, // 结束训练时当前题的id
  }
  await topicsInfoApi(data)
}

onLoad(async () => {
  if (JSON.stringify(props.practiceInfo) !== '{}') {
    uni.setNavigationBarTitle({
      title: props.practiceInfo.name,
    })
    /* 获取 所有题目数据 */
    await getTopicList()
  }
})
</script>

<style lang="scss" scoped>
/* 当前题 */
.currentTopic {
  height: calc(100vh - 332rpx);
  padding: 50rpx 34rpx;
  margin-top: 24rpx;
  overflow-y: auto;
  background: #fff;
  /* 题干 */
  .theTitle {
    /* 题型 */
    .quTypeName {
      padding: 6rpx 12rpx;
      margin-right: 12rpx;
      font-size: 24rpx;
      color: #fff;
      text-align: center;
      background: #3670f7;
      border-radius: 6rpx;
    }
    /* 内容 */
    & > text:nth-child(2),
    & > text:nth-child(3) {
      font-size: 30rpx;
      font-weight: 700;
      line-height: 2;
      color: #3d3d3d;
    }
  }
  /* 单选 */
  .wd-radio {
    justify-content: end;
  }
  /* 多选 */
  .wd-checkbox {
    align-items: center;
  }
  /* 文本域 */
  .wd-textarea {
    margin-top: 20rpx;
    font-size: 30px;
    font-weight: 500;
    color: #3d3d3d;
    border: 1rpx solid #cccccc;
    border-radius: 10rpx;
  }
  /* 文本域 - 标准答案 */
  /* stylelint-disable-next-line selector-type-no-unknown */
  .textarea-standardAnswer,
  :deep(uni-textarea) {
    height: 320rpx !important;
    max-height: 320rpx !important;
    overflow-y: auto;
  }

  .wd-radio,
  .wd-checkbox {
    box-sizing: border-box;
    display: flex;
    gap: 12rpx;
    width: 100%;
    padding: 20rpx;
    margin-top: 20rpx;
    font-size: 30px;
    font-weight: 500;
    color: #3d3d3d;
    border: 1rpx solid #cccccc;
    border-radius: 10rpx;
  }
  :deep(.wd-radio__shape),
  :deep(.wd-checkbox__shape) {
    width: 32rpx !important;
    height: 32rpx !important;
  }
  :deep(.wd-radio__label) {
    display: flex;
    flex: 1;
    justify-content: space-between;
  }
  :deep(.wd-checkbox__label) {
    flex: 1;
    .wd-checkbox__txt {
      display: flex;
      justify-content: space-between;
      width: 100%;
    }
  }
  /* 选中状态 */
  .is-checked {
    background: #e6e6e6;
  }
  /* 结束训练 */
  .end-practice-btn {
    color: #fe8f1e !important;
    border-color: #fe8f1e;
  }
  /* 确认答案 */
  .confirm-answer-btn {
    color: #fff;
    background: #fe8f1e;
  }
  .end-practice-btn,
  .confirm-answer-btn {
    font-size: 30rpx;
    font-weight: 700;
    border-radius: 8rpx;
  }
  /* 上一题/下一题 */
  .prev-btn,
  .next-btn {
    font-size: 30rpx;
    font-weight: 700;
  }
  /* 答案 */
  .isRight {
    font-size: 28rpx;
    font-weight: 700;
    color: #3d3d3d;
    white-space: nowrap;
  }
}
</style>
